package Queue_02;

/*
 * @Author 罗
 * @date 2020/3/11 - 12:30 上午
 *
 */

import java.lang.reflect.Array;
import java.util.Arrays;

public class Queue<T> {
    private int front;
    private int rear;
    private int maxSize;
    private T[] array;


    //    判断队列是否已满
    public boolean isFull() {
        return (rear + 1) % maxSize == front;
    }

    //    判断队列是否为空
    public boolean isEmpty() {
        return front == rear;
    }

    //    队列中有效元素的个数
    public int validCount() {
        return (rear + maxSize - front) % maxSize;
    }
    /*
     *   对队列进行优化
     *       1.将构造器中的front的含义进行一些调整：front指向队列的第一个元素，也就是说，array[front]就是队列的第一个元素
     *                   front 的初始值为 0
     *       2.将构造器中的rear的含义做一个调整：rear指向队列的最后一个元素的后一个位置，因为希望空出一个空间作为约定
     *                   rear 的初始值为 0
     *       3.当队列满的时候，条件为: (rear+1)%maxSize == front    (即，当rear在front之前时，队列已满)
     *       4.当队列为null的时候，条件为： front == rear
     *           (front+1)%maxSize == rear  表示，只剩一个元素了
     *       这种方法会预留array[maxSize-1]作为约定，这个空间永远不会被使用
     *           当rear == maxSize - 2 时，就达到了顶端，若front = 0，则说明空间已满
     *
     *       5.有效的数据个数就为(rear + maxSize - front) % maxSize
     *           中间的maxSize是为了当rear<front时，即rear在front前面时，获取到的结果不为负数
     * */


    //    入队列
    public void addQueue(T t) {
//        判断队列是否满
        if (isFull()) {
            throw new RuntimeException("队列已满");
        } else {
//            直接把数据加入
            this.array[rear] = t;
//              将rear后移，这里必须考虑取摸
            rear = (rear + 1) % maxSize;
        }
    }

    //  出队列
    public T getQueue() {
        if (isEmpty()) {
            throw new RuntimeException("队列为空，不能取数据");
        }
/*
    这里需要分析出front是指向队列的第一个元素
        1.先把array[front]保存到一个临时的变量
            如果直接返回的话，front就没有后移的机会了
*/
        Object temp = array[front];
//      2.将front的指针后移，(+1)代表不取数组中的最后一个元素（保留），取模以表示循环
        front = (front + 1) % maxSize;
//      3.将临时保存的变量返回
        return (T) temp;
    }

    //    遍历队列
    public void showQueue() {
        if (isEmpty()) {
            throw new RuntimeException("队列为null，不能输出数据");
        } else {
            System.out.print("我的方法:");
//            以front指针为起点，以rear指针为终点，期间使用循环数组
            for (int i = front; i != rear; i = (i + 1) % maxSize) {
                System.out.print("array[" + i + "]=" + array[i].toString() + "\t");
            }
            System.out.print("\n他的方法:");
//            以front为起点，front+数据的有效个数为终点，再通过 i%maxSize来取出想要的值
            for (int i = front; i < front + validCount(); i++) {
                System.out.print("array[" + (i % maxSize) + "]=" + array[i % maxSize].toString() + "\t");
            }
            System.out.println();
        }
    }

    public T getHead() {
        T head = array[front];
        front = (front + 1) % maxSize;
        return head;
    }

    //    显示队列的头数据，注意不是取出数据
    public T headQueue() {
        if (isEmpty()) {
            throw new RuntimeException("头数据为空，无法取出");
        } else {
//            +1是因为 front是指向队列头的前一个位置
            return (T) this.array[front];
        }
    }


    /******************之后为普通******************/

    /**
     * 使用类型标识
     * 在构造器中传入了 Class<T> 对象，通过 Array.newInstance(type, sz) 创建一个数组，
     * 这个方法会用参数中的 Class 对象作为数组元素的组件类型。
     * 这样创建出的数组的元素类型便不再是 Object，而是 T。
     * 这个方法返回 Object 对象，需要把它转型为数组。不过其他操作都不需要转型了，包括 get() 方法，
     * 因为数组的实际类型与 T[] 是一致的。这是比较推荐的创建泛型数组的方法。
     *
     * @param maxSize
     */
    public Queue(Class<T> type, int maxSize) {

        this.array = (T[]) Array.newInstance(type, maxSize);
        this.maxSize = maxSize;
        this.front = 0;
        this.rear = 0;
    }


    public Queue(int maxSize) {
        new Queue<Integer>(Integer.class, maxSize);
    }

    @Override
    public String toString() {
        return "Queue{" +
                "front=" + front +
                ", rear=" + rear +
                ", array=" + Arrays.toString(array) +
                '}';
    }

    public void setFront(int front) {
        this.front = front;
    }

    public void setRear(int rear) {
        this.rear = rear;
    }

    public void setArray(T[] array) {
        this.array = array;
    }

    public int getFront() {
        return front;
    }

    public int getRear() {
        return rear;
    }

    public T[] getArray() {
        return (T[]) array;
    }

    /****************自己写的 不好用**************/
    @Deprecated
    public void add(T t) throws Exception {
        if (rear == maxSize - 1) {
            if (front != 0) {
                rear = 0;
            } else {
                throw new Exception("队列已满");
            }

        }
        this.array[++rear] = t;
        if (rear == front) {
            throw new Exception("队列已满");
        }
        System.out.println("当前rear=" + rear + "\t当前front=" + front);
    }
    @Deprecated
    public T subtract() throws Exception {
        if (front == maxSize - 1) {
            if (rear != 0) {
                front = 0;
            } else {
                throw new Exception("数据已取完");
            }
        }
        if (front == rear) {
            throw new Exception("数据已取完");
        }
        System.out.println("出队列------->当前rear=" + rear + "\t当前front=" + (front + 1));
        return (T) this.array[++this.front];
    }
}
